Skip to content

Dogfood G4: return a typed attempt result to the requester on the issue event log - #13075

Merged
gunbai-bot[bot] merged 22 commits into
mainfrom
session/tidy-pike-588
Oct 4, 2026
Merged

gunbai-bot[bot] merged 22 commits into
mainfrom
session/tidy-pike-588

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

What this does

Dogfood vertical, stage G4 (result returned). The requester who assigned an issue to principal:gunbc/workflows/fabric only learned the outcome by reading the dashboard. The belt now appends ONE durable roadmap event per request-bound attempt when the attempt reaches a final outcome, so the issue's event history carries the answer and the issue page renders it.

This head answers the third side-chat NO-LAND ruling (on bca612b6), which approved LaunchContinuing as implemented and left one blocker: the result writer must derive the claim from the attempt's own durable binding. It builds on the second ruling's rework (three request/result-binding blockers and the stack ordering), described below. What both rulings said to keep is kept: ResultReturned { attempt, claim, outcome } with the recipient derived from the claim, the strict outcome decoder, anti-entropy over attempt history, pending classification for unreadable evidence / blocked integration or publication / yielded turns, one result per attempt at the append, causal ordering on the existing roadmap-events carrier, and combined reporting of result-return and later tail failures.

  • Event (gunbc.roadmap.roadmap_event_log): ResultReturned { attempt, claim, outcome }, AttemptResultOutcome = ResultPublished { pr, head } | ResultVerificationFailed { head, cause } | ResultHandedBack { reason }. It replaces the writer-less PublicationObserved.
  • Request record (gunbc.roadmap.roadmap_attempt_request_record): request-binding.json in the attempt state directory, written create-only; its type, codec, path, read and create. Launch binding (gunbc.roadmap.roadmap_attempt_request_binding): the launch classes and the admission that decides what is committed.
  • Writer (gunbc.roadmap.roadmap_result_returned result_return_attempts_for_instance, called from gunbc.roadmap_belt_tick_cli belt_run_once_cli_in), through gunbc.roadmap.roadmap_event_carrier roadmap_bound_result_append.
  • Reader: result_returned_observe_for_attempt(instance: HostDashboardInstance, node_id: String, attempt_key: String) -> ResultReturnedObservation with arms ResultReturnedObserved { node, event, claim, return_to, outcome, recorded_at } | ResultNotReturned | ResultNotOwed | ResultReturnedUnobserved. Pure core result_returned_observation_of(read, binding, node_id, attempt_key).
  • Record path: events/<node>/<event id>.json on the roadmap-events branch. No second result store.

Third ruling: the result writer derives the claim itself

  • roadmap_bound_result_append(instance, node, attempt, outcome, author, recorded_at) takes no claim, no binding and no event. Inside its private snapshot it reads the attempt's create-only request record, requires RequestClaimBound { claim }, and builds the RoadmapResultBinding itself (roadmap_result_authority_of_record). An absent, autonomous or unreadable record refuses (result-binding), as does a keyless attempt.
  • No effectful function in the carrier accepts a claim: the private half of the append takes the same issue and attempt and does the record read.
  • The module split the ruling described is made: roadmap_attempt_request_record (type, codec, path, read, create-only write; no event-carrier dependency) sits below the carrier; roadmap_attempt_request_binding (launch admission) and roadmap_event_carrier (bound append) both import it.
  • The result pass keeps its precheck for efficiency; it decides nothing about what is written.
  • Poisoning control, the_result_writer_derives_the_claim_from_the_attempts_record_so_a_later_claim_cannot_poison_it: K is bound to C1 and C2 is a later valid claim on the same issue. The authority established for K is C1; a raw K/C2 result offered to the generic append is refused and the log is unchanged; K/C1 is then admitted on the head.
  • The new append closes its snapshot through a match the result passes through. The same unused-let pattern in the carrier's older appends and reads is unchanged here; that carrier-wide cleanup is a follow-up, as the ruling says.
  • LaunchContinuing is unchanged from the ruled head.

Second ruling: the three blockers

1. The attempt carries the claim from the dispatch decision that admitted it

  • The assign route holds the Claimed event id the moment it appends the claim. Its follow-through now passes LaunchForClaim { node, claim } into belt_dispatch_node_for_instance_over, and that request is an explicit parameter down the spawn chain to belt_attempt_state_initialize_for_instance. Initialization no longer reads "the current claim".
  • The issue log is read at initialization only to ESTABLISH the carried claim (it must be a Claimed on that issue's history), never to choose one. A later claim C2 on the log changes nothing.
  • The binding is written with Filesystem.WriteCreateNew: absent commits; present and identical is an idempotent success; present and different, or unreadable, refuses.
  • A request naming another issue cannot bind the attempt.
  • Controls: a_launch_binds_the_claim_its_dispatch_decision_carried, a_committed_binding_is_created_once_and_cannot_be_replaced.

2. A request-bound launch whose claim cannot be read refuses the launch

  • There is no "unobserved" binding state any more. An unreadable issue log, or a forked or incomplete history, is RequestBindingNotAdmitted, which fails attempt-state initialization and so the spawn.
  • A dispatch nobody's claim caused (the timer, a bare /dispatch) that starts a lineage is its own class, LaunchAutonomous, recorded as RequestAutonomous.
  • At result time a bound claim the log no longer carries is a FAILED outcome (nonzero tick), no longer a quiet "unjoinable".
  • Control: a_request_bound_launch_refuses_when_its_claim_cannot_be_read.

3. Exact (node, attempt, claim) at append, anti-entropy and read

  • Append. The generic roadmap_event_append refuses every ResultReturned (step result-requires-binding). The only writer is roadmap_bound_result_append (see the third ruling above): it derives the identity from the attempt's record inside one private snapshot and decides with roadmap_bound_result_admission over every issue's events in that snapshot. It refuses a result for the attempt on another issue (result-elsewhere), a different result on this issue (result-duplicate), a claim that is not a Claimed on this issue (result-claim), and a history with no single head (result-history).
  • Anti-entropy. The population is every keyed attempt ref. An attempt is answered only by a result on its own issue naming its bound claim (result_attempt_answer); a result under its key on another issue, or naming another claim, does not take it out of the population.
  • Read. The reader takes the issue and the key, reads the attempt's binding, consults only that issue's history, and requires the result's claim to equal the bound claim.
  • Required reds: K/C1 with a result naming later claim C2, and K/C1 with a result on another issue, are each not K's answer and leave K owed (a_superseded_attempt_stays_owed_and_a_misaddressed_result_does_not_answer_it, a_misaddressed_unreadable_forked_or_duplicated_result_is_not_the_attempts_answer); a second result on a different issue is refused at the append (a_bound_result_is_refused_for_a_second_answer_a_foreign_claim_or_another_issue).

Stack ordering

The tick tail is result return, then served observation, then dogfood start record, as dependencies: the observation is taken inside a match on the result-return pass (every arm proceeds, so an undelivered result cannot freeze the page), and the start record runs only after a persisted observation. belt_tick_result_tail_exit is the one pure join and names both causes.

Continuations (ruled: approved as implemented)

  • A third launch class, LaunchContinuing { predecessor_attempt_key }, chosen by attempt_launch_request_for_origin when a dispatch nobody's claim caused turns out to continue an attempt. A dispatch FOR a claim that continues a lineage stays for that claim.
  • Initialization copies the predecessor attempt's recorded, create-only binding (same issue, by the path it is read from) into the new attempt's create-only binding. It reads no issue state.
  • A predecessor with no binding record, or an unreadable one, refuses the launch. The ruling accepts this as the migration boundary: a pre-change predecessor has no binding, a bare continuation refuses, and assigning the issue again launches under a new explicit claim.

Control: a_continuation_copies_its_predecessors_recorded_binding_or_refuses.

Things a reviewer should know

  • Attempts launched before this change get no result. They have no binding, so there is no requester to answer; the pass counts them as no-request and the reader answers ResultNotOwed.
  • Declared frontier: lineage-terminal answers. A lineage that ends without a candidate (a supervisor's plan-defect or stuck verdict, an exhausted escalation) is owed a result that no handoff state carries. Trigger: the change that gives the result pass the lineage's terminal verdict.
  • Yielded attempts return nothing (ruled correct by bold-bee-114): a yield ends a turn, not the request.
  • Request-bound launches read the issue log once, and the pass reads one small binding file per keyed attempt in the history on every tick.
  • Spawn-chain signatures changed. request: AttemptLaunchRequest is an explicit parameter on belt_dispatch_node_for_instance_over, belt_dispatch_node_staged_over, belt_actuate_spawn_for_instance, belt_actuate_spawn_exec_for_instance, belt_actuate_spawn_exec_modeled, belt_actuate_spawn_with_origin, belt_spawn_admitted and belt_attempt_state_initialize_for_instance.
  • Based on main again. roadmap: the dogfood route as one acceptance case (factory-dogfood-route) #13077 has landed; this branch merged main at 2dcaefad and the PR targets main, so its checks are PR-triggered. Earlier heads were stacked on roadmap/dogfood-route and were verified by manual dispatches of the workflow on the branch. The side chat ruled LAND at 2e2eea84; the one commit since that is not a merge is 98c39ff9 (review 75190: history ordering decided by an exhaustive match on the standing instead of its key string).
  • Tick exit: an undelivered result exits nonzero; not-terminal and no-request are counted and exit clean.

Not verified

  • No wet srv2 run. The rulings require one deployment receipt after landing: exact claim, exact bound attempt, terminal outcome, one ResultReturned, same claim/requester/outcome through the production reader.
  • That the belt tick can push roadmap-events on srv2 is inferred from the belt and serve units sharing a service user.
  • The carrier's git effects, Filesystem.WriteCreateNew at launch, and the serve route's hand-off of the claim id are not reachable from the hermetic floor. The claims cover the pure admission, commit, join, decision and read functions, both codecs, the carrier's decode, and the pass's observe-only gate.

Evidence (local claim_batch)

  • roadmap_result_returned_witness_test: 21/21. roadmap_event_log_witness_test: 24/24. roadmap_page_witness_test: 93/93. roadmap_event_carrier_witness_test: 3/3. roadmap_dogfood_route_witness_test: 21/21.
  • Mutation control, five planted defects in one run, each caught: an unreadable log launching as autonomous; a different binding being overwritten; any result under the key answering the attempt; the reader accepting a result naming another claim; a result on another issue not refusing the append. Five claims turned red, the rest stayed green. This run predates the continuation class and the third ruling's writer change.

🤖 Generated with Claude Code

…ute)

gunbc.roadmap_dogfood_route folds one attempt's durable records into a
closed DogfoodRouteReceipt over the eleven route stages (request, immutable
subject, shared computation, placement grant, isolated execution, durable
result, metering, cleanup, result returned, candidate published, independent
review). Completeness is an identity join: a stage missing or read twice is
malformed and named; one owed or refused stage withholds the receipt and is
named. Stages with a producer today are read off the attempt's own workflow
segments (no second reader); the six without one are typed Owed by the
change that will produce them (G1 shared computation, G3 metering, G4 result
returned, G5 placement/isolation/cleanup).

dogfood_route_acceptance_holds reads every srv2 attempt and holds when one
folds Complete -- the first such attempt is the dogfood start receipt
(operator ruling 2026-10-03). It is a plain function, not a floor witness:
its subject is a deployment's attempt records, which a CI runner lacks. The
roadmap row factory-dogfood-route binds it under the factory project.

Witness (supplied values at the two interfaces): positive control; owed,
refused, missing and duplicated stages; segment-to-reading mapping.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor Author

Addressed review 74515 in c4345b7: the reader's annotation no longer cites gunbc.roadmap_dogfood_route (which does not exist). It now names result_returned_report as the consumer in this change and states the dogfood receipt fold as a declared frontier whose trigger is the change that lands that module. Annotation-only edit; no behaviour change.

Brian Searls and others added 4 commits October 3, 2026 05:54
…e belt's selectors

Review 74522:
- Request and immutable subject no longer read records that belong to other
  stages (the environment admission; the verification verdict). Each is owed
  by the change that writes its own record: request by G4 (the Claimed event
  joined to the attempt), immutable subject by the G1 cutover (the exact tree
  the shared computation was keyed on). Durable result stays on the
  verification receipt, which is that record.
- DogfoodStageReadings is a product with one field per stage, so a stage
  missing or read twice has no constructor; the Malformed arm, the identity
  join and the stage-equality helper are deleted.
- Segments are selected by the belt's own exhaustive selectors
  (belt_verification_segment, belt_review_segment, and the new
  belt_publication_segment beside them) instead of a hand-numbered ordinal.
- The yes/no completeness predicate is inlined as a match at its one use.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review 74549:
- gunbc.roadmap_workflow_stage workflow_segment_of_kind (with
  workflow_segment_kind_eq read once through the kind's own key) replaces the
  hand-written per-kind folds: belt_verification_segment, belt_review_segment,
  belt_goal_audit_segment and the belt_publication_segment this PR had added
  are deleted and their callers rewritten; roadmap_attempt_continuation's
  verification_segment_of (no callers) is deleted with its now-unused kind
  imports. A new segment kind is one edit, not one per copy.
- The dogfood route's durable-result stage reads the SUBMISSION segment (the
  worker's candidate captured and committed at the head), not verification's
  verdict, so no stage rests on another stage's record.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… durable start receipt

Side-chat ruling on #13077 (NO-LAND at c55dd72), four findings:

1. The start receipt is selected and retained. dogfood_start_record_for_instance
   runs in the belt tick right after its passes and writes the first complete
   attempt ONCE, create-only, through the durable CAS store
   (file_compare_and_set, ExpectSlotAbsent) under <instance_root>/dogfood-start.
   The candidate-published stage is written by that same tick's publish pass,
   so the completing attempt is necessarily current when observed; once
   recorded, a newer attempt cannot move it. A refused write fails the tick
   loudly. The acceptance function now READS that receipt.
2. One record per stage is enforced by types: SubmissionEvidence,
   ReviewEvidence and PublicationEvidence are sole_constructor and built only
   from the producer's decoded record; each stage has its own reading type;
   stages with no producer have no Established arm (ProducerPending), so the
   route cannot complete until their producers land.
3. The route is declared in production order (review before publication: the
   belt publishes only a head whose review approved). The binding is the head:
   publication evidence requires its receipt's expected head to be the
   captured submission's head.
4. Witnesses exercise the real seams: the submission/review/publication
   readers over producer records (the publication receipt rendered by
   roadmap_publish's own encoder), cross-head publication refuses, the fold
   names the eight owed stages in route order, first-complete selection, and
   the start receipt round-trips through the acceptance read.

Single reader: gunbc.roadmap_belt_actuate exposes
belt_workflow_attempt_evidence_for_ref (the typed evidence the progress
projection reduces) and belt_workflow_attempt_evidences_observe_for_instance;
belt_workflow_progress_for_attempt_ref is its reduction.

ROADMAP.md regenerated for the factory-dogfood-route row.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…dence

Side-chat re-ruling on #13077 (NO-LAND at 6c9082a), three blockers:

1. The acceptance read decodes the whole receipt. DogfoodStartReceipt carries
   node, attempt, head, submission digest, review subject/plan/digest, PR
   number/url and an Rfc3339Timestamp; one join validation
   (dogfood_start_receipt_defect: review_subject == node@head, 40-hex head,
   positive PR, canonical RFC 3339 UTC instant, no empty identity) is run by
   the mint, the encoder and the total decoder. A refused clock refuses the
   record before the CAS; it is never written as the time.
2. The recorder scans durable attempt HISTORY: belt_workflow_attempt_evidence
   _for_key reads a named attempt (the current-pointer builder now delegates
   to it) and belt_workflow_attempt_history_evidences_observe_for_instance
   reads every dispatch ref by its own key. The scan stops once a valid
   receipt is in the slot. "First" is stated as the first complete attempt
   observed by a successful recorder; ties within a scan go by refname order
   (deterministic, not temporal).
3. One mint from evidence: dogfood_start_candidate builds the receipt from a
   single WorkflowAttemptEvidence and checks the joins; the encoder re-checks
   them, so correctness does not rest on sole_constructor (not enforced on
   the native route); review evidence carries an exact digest of the review
   report.

Witnesses: decoder accepts a complete receipt; rejects a same-schema
fragment, cross-field disagreement (subject node/head, bad head, PR 0, empty
node), refused/empty/non-canonical times; decoded receipt round-trips.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor Author

Reworked to the side-chat NO-LAND ruling on 3c70d77 in e75f127; the PR description is rewritten for this head. In short: (1) the attempt records its Claimed event at launch and ResultReturned carries that claim, with return_to read from that event; (2) the pass reconciles over the durable attempt history and runs even when other passes are withheld; (3) only final states return, ResultRefused is removed, and a second result for an attempt is refused by the decision, the carrier's append and the reader; plus the tick tail keeps both failure causes. Three files carry the refactor from #13077 applied as a patch. Not done: the srv2 deployment receipt the ruling requires after landing.

@gunbai-bot

gunbai-bot Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor Author

Re review 74752: verified. belt_workflow_attempt_history_evidences_observe_for_instance / BeltWorkflowAttemptEvidences have no caller on this branch, and the annotation on belt_workflow_attempt_evidence_for_key cites gunbc.roadmap_dogfood_route, which is not on this branch.

Not changing the code here, and why: those declarations are not this PR's. They are bold-bee-114's refactor from #13077, carried as a patch that its owner asked to be kept byte-identical so the two PRs do not fork one builder. Their consumer is gunbc.roadmap_dogfood_route, which #13077 adds. I have recorded them in the PR description as a declared frontier with that trigger (#13077 landing), and I am passing the finding to the owner so the annotation can be fixed at its source. This PR's own consumer of the patch is result_return_attempt, which calls belt_workflow_attempt_evidence_for_key.

If #13077 is not going to land, the right fix is to drop the unused observer and the citation from this PR, and I will.

— sent from tidy-pike-588

Brian Searls and others added 2 commits October 3, 2026 12:57
…ep while unproducible

Side-chat ruling on #13077 at 49f9cdd (NO-LAND), two blockers + hardening:

1. belt_workflow_attempt_evidence_for_key reads EVERY attempt-varying fact by
   the supplied key: admission, provider events and spawn failure now use
   dispatch_attempt_admission/events/spawn_failure_path_for_instance instead
   of the node's current projection. The provider-event projection is one
   argv over a path (dispatch_events_projection_argv_at,
   belt_provider_event_projection_observe_at); the current-pointer versions
   delegate. The validation summary stays node-level (the node's contract).
   Wet witness test.claim.roadmap_attempt_history_evidence_wet_witness_test
   plants K1 (historical) and K2 (current) with distinct sentinels: reading
   K1 by key returns only K1's, the current wrapper only K2's.
2. The recorder finds a candidate before reading the clock
   (dogfood_first_complete_evidence, then dogfood_start_step), and reads the
   clock only when a candidate exists. Producer-less stages live in one
   constant record (dogfood_unproduced) read by both the fold and the
   recorder; while it is non-empty the recorder answers NoCompleteAttempt
   without a history sweep. Pure controls: no candidate -> NoCandidate
   whatever the clock; candidate + absent/refused clock -> refused; canonical
   instant -> timestamp.
3. CasPreconditionFailed decodes the observed receipt before reporting
   AlreadyRecorded. Row text states the exact semantics (first complete
   attempt in refname order, by the first successful recorder).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ue's event log

Stacked on roadmap/dogfood-route (#13077) at b542857; this commit is the
whole G4 change on top of that branch.

- gunbc.roadmap.roadmap_attempt_request_binding: at launch the attempt
  records the Claimed event its issue is held under.
- gunbc.roadmap.roadmap_event_log: ResultReturned { attempt, claim,
  return_to, outcome } replaces the writer-less PublicationObserved; strict
  per-arm decoder; a second result for an attempt is not admitted.
- gunbc.roadmap.roadmap_event_carrier: the append asks that admission.
- gunbc.roadmap.roadmap_result_returned: final-state classification over
  CandidateHandoffState, the claim join, the pure write decision, the
  reconciliation over durable attempt history, and
  result_returned_observe_for_attempt.
- gunbc.roadmap_belt_tick_cli: the reconciliation runs on every tick; the
  tail exit keeps every failure cause.
- gunbc.roadmap_workflow_progress: the attempt row carries its handoff state.
- gunbc.roadmap_page: the issue stream renders every outcome.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot force-pushed the session/tidy-pike-588 branch from c58d519 to 9e52e23 Compare October 3, 2026 13:08
@gunbai-bot
gunbai-bot Bot changed the base branch from main to roadmap/dogfood-route October 3, 2026 13:08
@gunbai-bot

gunbai-bot Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor Author

Re review 74752: resolved by stacking, at 9e52e23. This PR is now based on roadmap/dogfood-route (#13077) and no longer carries a copy of that PR's evidence-builder refactor, so the unused belt_workflow_attempt_history_evidences_observe_for_instance and the citation of gunbc.roadmap_dogfood_route are not in this PR's diff: both come from the base, where the module and its caller exist. The diff is 11 files, all G4. It retargets to main and lands after #13077. This supersedes my earlier reply that left the code unchanged. The branch was force-pushed to restack it.

…terface

The floor runs witnesses hermetically and shell.Mktemp.DirWithTemplate has no
hermetic arm, so the wet two-attempt witness never reached its subject
(route_gap). Rather than enrol it as route-gap debt, the keyed addressing the
side chat's finding is about is extracted into one pure function,
belt_attempt_record_paths (admission, provider events, spawn failure for one
named attempt), which the evidence builder reads through, and witnessed at
that interface with supplied values: K1's paths name K1's directory, two
attempts never share a record path, and a historical attempt never resolves
through the node's current-attempt projection. The builder's real route runs
in production via the belt's history scan.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brian Searls and others added 3 commits October 3, 2026 17:03
# Conflicts:
#	ROADMAP.md
#	dag/gunbc/roadmap/roadmap_belt_actuate.dag
… the event

Review 74788: the event carried return_to beside the claim it names; the
standing fold trusted the copy while only the reader checked it against
the claim, so one fact had two homes that could disagree.

ResultReturned is now { attempt, claim, outcome }. The fold, the reader and
the page read the recipient from the claim via roadmap_claim_return_to.
The carrier's append admission also refuses a result whose claim is not a
Claimed on the same history, so a result with no readable recipient cannot
be written.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor Author

Re review 74788: fixed in 66d2938 by the first option. ResultReturned no longer carries return_to; it is { attempt, claim, outcome }. The standing fold's handoff, the reader and the page all read the recipient from the claim the event names (roadmap_claim_return_to), so there is no copy to disagree with its source. roadmap_event_admitted_after additionally refuses a result whose claim is not a Claimed on the same history, so a published result with no readable recipient cannot be appended; a fold over a history that somehow holds one moves no holder. Witnessed in a_second_result_for_an_answered_attempt_is_not_admitted and the_publication_handoff_reassigns_to_the_return_to_of_the_results_claim. The head also merges roadmap/dogfood-route at e7e9ca9, which replaces the two wet witnesses that made the floor refuse on 9e52e23.

Review 74930: belt_run_once_cli_note was a data String no program reads,
and this change had added an exit rule to it. The text is now the leading
annotation on belt_run_once_cli and the row is gone; the one citation of
the row's name now names the entry.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor Author

Re review 74930: fixed in 529766c. belt_run_once_cli_note is deleted; its text, including the exit rule this PR added, is now the leading annotation on belt_run_once_cli. The one mention of the row's name, a comment in roadmap_publication_helper.dag, now cites gunbc.roadmap_belt_tick_cli belt_run_once_cli.

CI note for reviewers: this PR is stacked on roadmap/dogfood-route, and the witnesses workflow only triggers for pull requests based on main, so no check appears here. The previous head 66d2938 was verified by a manual dispatch on the branch, run 37143569880 (floor, generated, emit-build, witnesses all success). A new dispatch is running for this head.

Brian Searls and others added 5 commits October 3, 2026 19:53
…onsumed

Side-chat NO-LAND at e7e9ca9, two blockers:

1. The start-receipt CAS root was never provisioned. The slot now lives in
   the instance's attempt-state root, which the deployment's AttemptStateRoot
   member converges (gunbc.live_deploy.spec deployment_owned_steps). The tick
   tail (belt_tick_tail_exit) writes the served observation before a refused
   start record fails the tick. Wet controls run on a fresh instance layout,
   enrolled on the local-repo wet lane (exclusion row, wet schedule,
   route-gap chunk 33):
   - an unconverged root refuses;
   - a converged root reads absent and the tick tail succeeds;
   - the first create commits and a competing create decodes the winner;
   - an invalid winner refuses.

2. The landed G5 reader is consumed. WorkflowAttemptEvidence carries one
   SessionPlacementObservation, read by the supplied attempt key in
   belt_workflow_attempt_evidence_for_key.
   - placement-grant is established from a recorded grant: host, unit,
     grant identity and fence.
   - cleanup is established from a settled release bound to that grant.
   - isolated-execution stays owed to a named producer, because the slot
     and record do not establish that the capped unit ran.
   - dogfood_unproduced now has six stages, and the route witness asserts
     the same six.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Conflict in roadmap_belt_tick_cli.dag: that branch added its own
belt_tick_tail_exit(served, start). It is kept unchanged; the result-return
exit is renamed belt_tick_result_tail_exit and wraps it, so the result
return still runs first on every tick and the exit names every cause.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rved-before-recorder dependency

Side-chat NO-LAND at 845ed0e:
1. DogfoodRouteComplete carries placement and cleanup evidence; dogfood_route_complete_of
   refuses cleanup of another grant/fence; the start receipt (schema v2) carries grant
   identity, fence and cleanup outcome through wire, decoder and defect check.
2. The raw-wire writer is gone: the create-only write is inline in the evidence-derived
   recorder. Wet claims plant through the generic CAS primitive and ask what the recorder
   and acceptance read make of a valid or invalid occupied slot.
3. The tick's tail matches on the served observation's exit before the recorder runs;
   belt_start_record_exit maps only the recorder's outcome; the exit contract names it.
4. The roadmap authority row states six owed stages, G5 placement and cleanup produced.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts:
#	src/v2/workflow/floor_route_gap.dag
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor Author

Ruling

NO-LAND #13075 at d74ed557625492839e0eae513d630e0f7e6f11f6.

This ruling is independent of #13077’s outstanding repairs. Do not queue a future retargeted head without fixing the G4 issues below.

The exact stacked head is mergeable against roadmap/dogfood-route, and dispatched run 37150608302 completed floor, generated, emit-build, and aggregate witnesses successfully. fileciteturn291file0L2-L16 fileciteturn300file0L1-L2

The prior three findings are much closer:

  • the pass enumerates durable attempt refs rather than current attempts;
  • retryable handoff observations remain pending;
  • one issue history admits at most one result per attempt;
  • the production writer uses a launch-time claim record rather than the issue’s assignment at completion.

Three request/result-binding blockers remain.

1. The attempt does not carry the claim that caused its dispatch

The request claim is not an input to launch admission or the spawn plan. During attempt-state initialization, the code rereads the issue log and binds whichever claim is current at that later moment:

roadmap_events_read(...)
→ roadmap_issue_current_claim(...)
→ RequestClaimBound

This leaves the following race:

C1 causes attempt K to be selected
C2 claims/reassigns the issue
K initializes its state
K records C2

K’s result is now addressed to C2 even though C1 caused K.

The stored binding is also not immutable. attempt_request_binding_record_for_instance uses ordinary Filesystem.Write, so another execution of state initialization can replace the original claim with a later one. The source calls the record “fixed at launch,” but neither the claim provenance nor write mechanism establishes that property. fileciteturn302file0

This must be a fact carried from the exact dispatch decision that admitted K, not a second read of current issue state. The attempt initialization should receive a typed request binding containing the claim event ID from that decision and persist it create-only:

absent → commit C1
present C1 → idempotent success
present anything else → refuse

Required controls:

  1. C2 arrives after K is selected but before state initialization; K remains bound to C1.
  2. Reinitializing K under C2 cannot replace C1.
  3. A binding from another node or another dispatch decision cannot initialize K.

2. An unreadable request log still launches work that can never return a result

When the issue history is unreadable, forked, or incomplete, initialization writes:

RequestClaimUnobserved { reason }

That is still RequestBindingRecorded, and belt_attempt_state_initialize_for_instance treats every recorded arm as AttemptStateInitialized; the worker may start. fileciteturn302file0 fileciteturn293file0

At completion, that same record becomes ResultRequestUnjoinable. result_return_pass_failure explicitly treats ResultReturnUnjoinable as success, so the tick remains green and no result event is ever produced. fileciteturn293file0

This is a permanent loss of the exact obligation G4 exists to discharge:

transient log read failure at launch
→ expensive attempt runs
→ terminal answer exists
→ no requester can be identified
→ no result is written
→ every later tick exits successfully

For a request-bound worker attempt, inability to establish the exact claim must refuse launch. It cannot be recorded as a permanent “unobserved” fact and then treated as successful initialization.

A genuinely autonomous attempt with no requester can have a separate typed launch class and remain outside G4. Do not collapse that case with a request whose identity could not be observed.

3. A result event is not checked against the attempt’s binding

The shared event carrier admits a ResultReturned when:

  • that issue has no result yet for the attempt key; and
  • the event’s claim is any Claimed event on that issue.

It never reads the attempt’s request-binding.json or checks that event.claim equals the claim bound to the attempt. fileciteturn304file0

The reader is weaker still:

result_returned_observe_for_attempt(instance, attempt_key)

It receives no expected node and reads no request binding. It searches all issue logs by attempt key, selects the sole issue containing such a result, and accepts the result’s own claim. fileciteturn303file0 fileciteturn293file0

Therefore this bad history remains constructible:

attempt K is bound to issue A / claim C1

append on issue A:
  ResultReturned { attempt: K, claim: later claim C2, ... }

or append on issue B:
  ResultReturned { attempt: K, claim: valid claim B1, ... }

Both pass the carrier’s current admission if the named claim is a Claimed event on that issue. The reader then returns the answer to C2 or B1.

Worse, the anti-entropy population considers K answered when any result event with key K exists anywhere:

count(roadmap_results_for_attempt(all_events, K)) == 0

So the misaddressed event removes K from the open population and permanently prevents the correct C1 result from being written. fileciteturn293file0

This disproves the PR description’s assertion that a misaddressed result is no longer writable.

Required repair

The authoritative identity is the full tuple:

(node_id, attempt_key, bound_claim)

Both writing and reading must establish it.

  • Change the reader to something equivalent to:
result_returned_observe_for_attempt(instance, node_id, attempt_key)
  • Read that attempt’s request binding.
  • Require the result event to be on node_id.
  • Require its claim to equal the bound claim exactly.
  • Filter open attempts by the bound node and attempt, not by a free attempt-key match across all issues.
  • Prevent generic roadmap_event_append from admitting a raw ResultReturned without this binding proof. A specialized bound-result append is the cleaner shape.

Required reds:

  1. K/C1 with a result naming a later same-issue claim C2.
  2. K/C1 with a result on another issue naming its valid claim.
  3. Either planted event must remain unreadable as K’s answer and must not remove K from anti-entropy.
  4. A second result on a different issue must be refused, not merely diagnosed later as ambiguous.

Stack integration requirement

The current merged tail executes, by data dependencies:

result return
→ dogfood start recorder
→ served observation

belt_tick_result_tail_exit and belt_tick_tail_exit combine already-computed outcomes; they do not impose another effect order. fileciteturn305file0

That inherits the outstanding #13077 issue: a long-running or aborting start recorder can still prevent the served observation from running. After #13077 is repaired, the integrated ordering should be explicit:

result return
→ served observation
→ dogfood start recorder

That gives the dashboard the newly returned result while ensuring the start recorder cannot freeze the page. The retargeted head must preserve this as an actual dependency, not as eager argument evaluation plus comments.

What should remain

Keep:

  • ResultReturned { attempt, claim, outcome }, with the recipient derived from the claim rather than copied;
  • the strict outcome decoder;
  • historical anti-entropy rather than current-attempt selection;
  • pending classification for unreadable evidence, blocked integration/publication, and yielded turns;
  • the append-time prohibition on a second result;
  • causal event ordering and the existing roadmap-events carrier;
  • the combined reporting of result-return and later tail failures.

The missing srv2 wet receipt is not the current code-review blocker. After the three binding repairs and the corrected stack ordering, require the production receipt already identified in the PR:

exact claim
→ exact bound attempt
→ terminal outcome
→ one ResultReturned
→ same claim/requester/outcome through the production reader

Reapproval bar: immutable dispatch-carried claim binding; launch refusal when that binding is unobservable; exact (node, attempt, claim) enforcement at append, anti-entropy, and read; then retarget onto the corrected #13077 head and rerun the four required jobs.

gunbc-ci-auto-heal and others added 2 commits October 3, 2026 21:56
Conflict in roadmap_belt_tick_cli.dag. The tick tail is now result return,
then served observation, then start record, as dependencies: the
observation is taken inside a match on the result-return pass (every arm
proceeds, so an undelivered result cannot freeze the page), and the start
record runs only after a persisted observation, through that branch's
belt_start_record_exit. belt_tick_result_tail_exit remains the one pure
join that names both causes. The exit annotation carries the new
start-record sentence.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nly binding, exact identity

Side-chat NO-LAND ruling on d74ed55.

1. The attempt carries the claim from the dispatch decision. The assign
   route passes LaunchForClaim { node, claim } into the dispatch it
   triggers; the request is an explicit parameter down the spawn chain to
   attempt-state initialization, which no longer reads the issue's current
   claim. The binding is written with Filesystem.WriteCreateNew: identical
   is idempotent, different refuses.

2. A request-bound launch whose claim cannot be established refuses the
   launch. The unobserved binding state is gone. A bound claim the log no
   longer carries at result time is a failed outcome.

3. Exact (node, attempt, claim). The generic append refuses every
   ResultReturned; roadmap_bound_result_append builds the event from the
   binding inside one snapshot and refuses a result elsewhere, a second
   result, a foreign claim, or a forked history. Anti-entropy treats an
   attempt as answered only by a result on its issue naming its bound
   claim. The reader takes the issue and the key and reads the binding.

Continuations: a dispatch no claim caused that continues an attempt is
LaunchContinuing and copies the predecessor's recorded binding; a
predecessor without a readable binding refuses the launch.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor Author

Reworked to the second NO-LAND ruling (on d74ed55) in bca612b; the PR description is rewritten for this head.

  1. Claim from the dispatch decision. The assign route passes LaunchForClaim { node, claim } (the id of the Claimed it just appended) into the dispatch it triggers; that request is an explicit parameter down the spawn chain to attempt-state initialization, which no longer reads the issue's current claim. The binding is written with Filesystem.WriteCreateNew: identical is idempotent, different refuses.
  2. Launch refusal. A request-bound launch whose claim cannot be established (unreadable log, forked history, claim not on the issue) fails initialization. The unobserved binding state is deleted.
  3. Exact (node, attempt, claim). The generic append refuses every ResultReturned; roadmap_bound_result_append builds the event from the binding in one snapshot and refuses a result on another issue, a second result, a foreign claim, or a forked history. Anti-entropy counts an attempt as answered only by a result on its issue naming its bound claim. The reader is now result_returned_observe_for_attempt(instance, node_id, attempt_key) and reads the binding.
  4. Stack ordering. Result return, then served observation, then start record, as dependencies.

One point for the side chat to rule on, set out in the PR description under "continuations": a dispatch no claim caused that continues an attempt is LaunchContinuing and copies the predecessor's recorded create-only binding, refusing when the predecessor has none. The fallback if that is rejected is to treat it as autonomous with no result.

CI: stacked PRs get no PR-triggered run; dispatch 37160413666 is running on this head. Not done: the srv2 deployment receipt.

— sent from tidy-pike-588

@gunbai-bot

gunbai-bot Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor Author

Ruling

NO-LAND #13075 at bca612b6c9d47616e13a66b055040f0a558fe70b.

The exact stacked head is mergeable against roadmap/dogfood-route. Dispatched run 37160413666 completed floor, generated, emit-build, and aggregate witnesses successfully on this SHA. fileciteturn406file0L2-L17 fileciteturn431file0L1-L2

The continuation design is correct. One binding-authority blocker remains.

Continuation ruling: approve LaunchContinuing

A continuation should not become autonomous merely because no new claim caused the continuation dispatch. It is another turn of an existing request lineage, and dropping its predecessor’s binding would make the lineage’s eventual answer unreturnable.

The implemented distinction is sound:

new claim caused dispatch
  → LaunchForClaim { new claim }
  → even if it continues an existing branch, answer the new claim

no claim caused dispatch + no predecessor
  → LaunchAutonomous

no claim caused dispatch + predecessor
  → LaunchContinuing { predecessor attempt }
  → copy the predecessor’s durable binding

The continuation reads the predecessor’s binding at the same node’s attempt-state path. It does not inspect the issue’s current assignment, so a later claim cannot enter through the side door. Missing or unreadable predecessor evidence refuses rather than silently widening to autonomous. This matches the continuation model: ContinueFromAttempt is explicitly the next turn of the predecessor’s lineage, not a new independent request. fileciteturn420file0L1-L2

I also accept the deployment consequence:

  • a pre-change predecessor has no binding;
  • a bare continuation therefore refuses;
  • reassigning the issue establishes a new explicit claim and launches under it.

That is an honest migration boundary. Do not replace it with a declared gap or an autonomous fallback.

The launch ordering is now correct as well. Attempt-state initialization commits the request binding before publishing the current-attempt pointer and before starting the worker container. A request-binding refusal may leave prepared state/worktree artifacts, but it cannot start an unanswerable worker. fileciteturn422file0L1-L2 fileciteturn423file0L1-L2

Blocker: the specialized append still trusts a caller-authored binding

The production result pass now reads request-binding.json and constructs the correct:

RoadmapResultBinding {
  node,
  attempt,
  claim
}

But the effect boundary does not establish that fact itself.

RoadmapResultBinding is an ordinary public record, and:

roadmap_bound_result_append(
  layout,
  binding: RoadmapResultBinding,
  outcome,
  ...
)

accepts it directly. The specialized append checks:

  • the claim is a real Claimed event on the named issue;
  • no result already exists for the attempt;
  • the issue history has one head.

It does not read the attempt’s durable request binding or prove that the supplied claim is the claim that attempt recorded at launch. The generic append is closed, but the one bypass around it still trusts exactly the tuple that needed authoritative binding. fileciteturn427file0L1-L2

Representable poisoning sequence

Suppose:

attempt K request-binding.json = claim C1
claim C2 is a later, valid Claimed event on the same issue

Any caller can submit:

roadmap_bound_result_append(
  binding = { node: N, attempt: K, claim: C2 },
  outcome = ...
)

The append admits it because C2 is a valid claim on N and K has no prior result.

Afterward:

  1. The exact reader reads K’s binding as C1.
  2. The C2 result does not answer K, so the reader reports a binding mismatch.
  3. Anti-entropy correctly keeps K owed.
  4. The pass tries to append the correct C1 result.
  5. Admission finds an existing result for K and refuses the correct result as result-duplicate.

The wrong event has permanently poisoned the attempt’s result slot. Every later tick can continue failing while the actual requester never receives the answer.

The present production caller behaves correctly, but “the only current caller passes the right value” is not the same as “the wrong result cannot be written.” The witness suite also freely constructs RoadmapResultBinding, which confirms there is no provenance wall at this boundary.

This means the third blocker from the prior ruling—exact (node, attempt, claim) enforcement at append—is not yet fully closed.

Required repair

The append API should not accept a claim from its caller.

The strongest shape is:

roadmap_bound_result_append(
  instance,
  node,
  attempt,
  outcome,
  author,
  recorded_at,
)

Inside the effect boundary:

  1. Read the exact attempt’s create-only request-binding record.
  2. Require RequestClaimBound { claim }.
  3. Refuse absent, autonomous, unreadable, or malformed bindings.
  4. Build RoadmapResultBinding internally.
  5. Run the existing whole-log admission and conditional publication.

The higher result pass may keep its current precheck for efficiency, but the writer must re-establish the binding before committing the event.

There is currently an import cycle in the obvious placement: the request-binding module reads the event carrier to establish a claim at launch. The clean split is:

roadmap_attempt_request_record
  - AttemptRequestBinding
  - codec
  - path
  - read/create-only record operations
  - no event-carrier dependency

roadmap_attempt_request_binding
  - launch-time claim admission
  - imports record module + event carrier

roadmap_event_carrier
  - bound result append
  - imports record module
  - reads the binding and derives the claim

Do not repair this solely with sole_constructor or admit_callers; the standing native-route sealing gaps make those defense in depth, not the correctness boundary.

Add the discriminating control at the append-authority seam:

K durably bound to C1
C2 is also a valid claim on N

attempt to write K/C2
  → refuse without occupying K’s result

attempt to write K/C1
  → admit

What is closed and should remain

The rest of the rework is the right G4 shape:

  • The assignment route carries the exact appended claim into dispatch.
  • A later claim cannot replace a committed binding.
  • Request-bound launch refuses when the named claim cannot be established.
  • Autonomous launches are explicit rather than failed observations disguised as autonomy.
  • Historical anti-entropy considers every durable keyed attempt.
  • The reader uses exact node, attempt, and durable binding.
  • Generic roadmap_event_append refuses all raw ResultReturned events.
  • Retryable handoff states remain pending.
  • The tick orders result reconciliation before served observation, then dogfood-start recording, while preserving both failure causes.

Non-blocking carrier cleanup

The new bound append repeats the carrier’s existing pattern:

let result = ...
let cleanup = roadmap_event_snapshot_close(...)
result

An unused binding is not a reliable effect dependency in this language. Sequence snapshot closure through a consumed match in a carrier-wide follow-up so result appends do not leak private worktrees. This is not the reason for the present NO-LAND ruling, but the new result path will exercise that standing defect.

Reapproval bar

  1. The result-append effect reads the exact durable attempt binding and derives the claim itself.
  2. A caller cannot supply or substitute the claim committed to the event.
  3. The C1/C2 poisoning control refuses C2 and leaves C1 appendable.
  4. After roadmap: the dogfood route as one acceptance case (factory-dogfood-route) #13077 lands, retarget to main and rerun all four required jobs.

Keep LaunchContinuing exactly as implemented. The remaining change is at the result writer, not the continuation model.

…t's own record

Side-chat NO-LAND ruling on bca612b: roadmap_bound_result_append still
accepted a caller-authored binding, so a result for attempt K naming a
later valid claim C2 could be written and would permanently occupy K's
result slot.

roadmap_bound_result_append(instance, node, attempt, outcome, author,
recorded_at) takes no claim, binding or event. Inside its snapshot it reads
the attempt's create-only request record and derives the claim
(roadmap_result_authority_of_record); an absent, autonomous or unreadable
record refuses. No effectful carrier function accepts a claim.

The record's type, codec, path, read and create-only write move to
gunbc.roadmap.roadmap_attempt_request_record, below the carrier, so the
launch admission and the bound append both read it without a cycle.

The new append closes its snapshot through a consumed match.
LaunchContinuing is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor Author

Reworked to the third NO-LAND ruling (on bca612b) in 2e2eea8; the PR description is updated.

  • roadmap_bound_result_append(instance, node, attempt, outcome, author, recorded_at) no longer accepts a claim, a binding or an event. Inside its private snapshot it reads the attempt's create-only request record, requires RequestClaimBound { claim }, and builds the result identity itself (roadmap_result_authority_of_record). Absent, autonomous and unreadable records refuse. No effectful carrier function takes a claim.
  • The module split the ruling described is made: gunbc.roadmap.roadmap_attempt_request_record (type, codec, path, read, create-only write; no carrier dependency) sits below the carrier and is imported by both the launch admission and the bound append.
  • C1/C2 poisoning control: the_result_writer_derives_the_claim_from_the_attempts_record_so_a_later_claim_cannot_poison_it. K bound to C1, C2 a later valid claim on the same issue: the authority established is C1; a raw K/C2 result is refused by the generic append and the log is unchanged; K/C1 is then admitted.
  • The new append closes its snapshot through a consumed match. The older appends and reads in the carrier keep the unused-let pattern; that is the carrier-wide follow-up the ruling named and is not in this PR.
  • LaunchContinuing is unchanged.

A dispatched CI run is in progress on this head. After #13077 lands: retarget to main and rerun all four jobs. — sent from tidy-pike-588

Review 75190: the launch admission (and the result reader and admission)
asked whether a history can be ordered by comparing roadmap_node_standing_key
to two strings. roadmap_history_unordered_reason matches RoadmapNodeStanding
exhaustively, and all three callers use it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor Author

Re review 75190: fixed in 98c39ff. Verified the finding: attempt_request_binding_admission decided 'this history cannot be ordered' by comparing roadmap_node_standing_key against two strings, and the same comparison was in the result module's history check. There is now one typed function, roadmap_history_unordered_reason in gunbc.roadmap.roadmap_event_log, which matches RoadmapNodeStanding exhaustively (NodeForked and NodeHistoryIncomplete refuse; every other arm is named). The launch admission, the result reader and roadmap_bound_result_admission all call it, so a new standing arm has to be placed before any of them compiles.

Not changed: the same string comparison in code this PR does not touch (roadmap_event_parent_admitted in the carrier and roadmap_event_record_cli). A dispatched CI run is in progress on this head.

Base automatically changed from roadmap/dogfood-route to main October 4, 2026 02:05
#13077 was squash-merged, so the two files both sides carried conflicted
textually. ROADMAP.md is generated and untouched by this PR: main's copy.
roadmap_belt_tick_cli.dag on main is identical to the old base branch's,
so this branch's copy (base plus the result-return tail) stands unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 4, 2026
Merged via the queue into main with commit 9b278bd Oct 4, 2026
4 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/tidy-pike-588 branch October 4, 2026 06:32
@briansrls
briansrls restored the session/tidy-pike-588 branch October 4, 2026 06:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants